#include <iostream>
#include "cbptarget.h"
#include "cbhelper.h"
#include "stlconvert.h"
#include "stlfutils.h"
#include "tinyxml.h"

CBuildTarget::CBuildTarget( void ) {
  Clear();
}

CBuildTarget::~CBuildTarget( void ) {
  Clear();
}

CString CBuildTarget::TargetTypeName( const TargetType Type ) {
  switch( Type ) {
    case CBuildTarget::ttExecutable:
      return "executable";
    case CBuildTarget::ttConsoleExecutable:
      return "console executable";
    case CBuildTarget::ttStaticLibrary:
      return "static library";
    case CBuildTarget::ttDynamicLibrary:
      return "dynamic library";
    case CBuildTarget::ttNative:
      return "native";
    case CBuildTarget::ttCommands:
      return "commands";
  }
  return "";
}

CString CBuildTarget::TargetTypeName( void ) {
  return TargetTypeName( m_Type );
}

CString CBuildTarget::OptionsRelationName( const OptionsRelation Relation ) {
  switch( Relation ) {
    case CBuildTarget::orProject:
      return "project";
    case CBuildTarget::orTarget:
      return "target";
    case CBuildTarget::orTargetProject:
      return "target project";
    case CBuildTarget::orProjectTarget:
      return "project target";
  }
  return "";
}

CString CBuildTarget::AutoFilePrefix( const CPlatform::OS_Type OS ) {
  switch( OS ) {
    default:
    case CPlatform::OS_Unix: {
      switch( m_Type ) {
        case CBuildTarget::ttConsoleExecutable: {
          return "";
        }
        case CBuildTarget::ttExecutable:        {
          return "";
        }
        case CBuildTarget::ttStaticLibrary:     {
          return "lib";
        }
        case CBuildTarget::ttDynamicLibrary:    {
          return "";
        }
        case CBuildTarget::ttNative:            {
          return "";
        }
        case CBuildTarget::ttCommands:          {
          return "";
        }
      }
      break;
    }
    case CPlatform::OS_Windows: {
      switch( m_Type ) {
        case CBuildTarget::ttConsoleExecutable: {
          return "";
        }
        case CBuildTarget::ttExecutable:        {
          return "";
        }
        case CBuildTarget::ttStaticLibrary:     {
          return "lib";
        }
        case CBuildTarget::ttDynamicLibrary:    {
          return "";
        }
        case CBuildTarget::ttNative:            {
          return "";
        }
        case CBuildTarget::ttCommands:          {
          return "";
        }
      }
      break;
    }
    case CPlatform::OS_Mac: {
      switch( m_Type ) {
        case CBuildTarget::ttConsoleExecutable: {
          return "";
        }
        case CBuildTarget::ttExecutable:        {
          return "";
        }
        case CBuildTarget::ttStaticLibrary:     {
          return "lib";
        }
        case CBuildTarget::ttDynamicLibrary:    {
          return "";
        }
        case CBuildTarget::ttNative:            {
          return "";
        }
        case CBuildTarget::ttCommands:          {
          return "";
        }
      }
      break;
    }
  }
  return "";
}

CString CBuildTarget::AutoFileExtension( const CPlatform::OS_Type OS, const CBuildTarget::TargetType Type ) {
  switch( OS ) {
    default:
    case CPlatform::OS_Unix: {
      switch( Type ) {
        case CBuildTarget::ttConsoleExecutable: {
          return "";
        }
        case CBuildTarget::ttExecutable:        {
          return "";
        }
        case CBuildTarget::ttStaticLibrary:     {
          return "a";
        }
        case CBuildTarget::ttDynamicLibrary:    {
          return "so";
        }
        case CBuildTarget::ttNative:            {
          return "";
        }
        case CBuildTarget::ttCommands:          {
          return "";
        }
      }
      break;
    }
    case CPlatform::OS_MSys:
    case CPlatform::OS_Windows: {
      switch( Type ) {
        case CBuildTarget::ttConsoleExecutable: {
          return "exe";
        }
        case CBuildTarget::ttExecutable:        {
          return "exe";
        }
        case CBuildTarget::ttStaticLibrary:     {
          return "a";
        }
        case CBuildTarget::ttDynamicLibrary:    {
          return "dll";
        }
        case CBuildTarget::ttNative:            {
          return "sys";
        }
        case CBuildTarget::ttCommands:          {
          return "";
        }
      }
      break;
    }
    case CPlatform::OS_Mac: {
      switch( Type ) {
        case CBuildTarget::ttConsoleExecutable: {
          return "";
        }
        case CBuildTarget::ttExecutable:        {
          return "";
        }
        case CBuildTarget::ttStaticLibrary:     {
          return "a";
        }
        case CBuildTarget::ttDynamicLibrary:    {
          return "dylib";
        }
        case CBuildTarget::ttNative:            {
          return "";
        }
        case CBuildTarget::ttCommands:          {
          return "";
        }
      }
      break;
    }
  }
  return "";
}

CString CBuildTarget::AutoFileExtension( CString& Platform ) {
  return AutoFileExtension( CPlatform::OS( Platform ), m_Type );
}

CString CBuildTarget::AutoFileExtension( const int Platform ) {
  return AutoFileExtension( m_Platforms.GetString( Platform ) );
}

CString CBuildTarget::MakeOptions( const OptionsRelation Relation,
                                   const CString& ProjectOptions, const CString& TargetOptions ) {
  switch( Relation ) {
    case CBuildTarget::orProject:
      return ProjectOptions;
    case CBuildTarget::orTarget:
      return TargetOptions;
    case CBuildTarget::orTargetProject:
      return JoinStr( TargetOptions, ProjectOptions, ' ' );
    default:
    case CBuildTarget::orProjectTarget:
      return JoinStr( ProjectOptions, TargetOptions, ' ' );
  }
}

CString CBuildTarget::CFlags( void ) {
  CString result;
  for( int i = 0; i < m_CompilerOptions.GetCount(); i++ ) {
    result += " " + m_CompilerOptions[i];
  }
  return result;
}

CString CBuildTarget::CFlags( const CString& ProjectCFlags ) {
  return MakeOptions( m_CompilerOptionsRelation, ProjectCFlags, CFlags() );
}

CString CBuildTarget::RCFlags( void ) {
  CString result;
  for( int i = 0; i < m_ResourceCompilerOptions.GetCount(); i++ ) {
    result += " " + m_ResourceCompilerOptions[i];
  }
  return result;
}

CString CBuildTarget::RCFlags( const CString& ProjectRCFlags ) {
  return MakeOptions( m_ResourceCompilerOptionsRelation, ProjectRCFlags, RCFlags() );
}

CString CBuildTarget::LdFlags( void ) {
  CString result;
  for( int i = 0; i < m_LinkerOptions.GetCount(); i++ ) {
    result += " " + m_LinkerOptions[i];
  }
  return result;
}

CString CBuildTarget::LdFlags( const CString& ProjectLdFlags ) {
  return MakeOptions( m_LinkerOptionsRelation, ProjectLdFlags, LdFlags() );
}

CString CBuildTarget::IncDirs( const CString& IncDirSwitch ) {
  CString result;
  for( int i = 0; i < m_CompilerDirectories.GetCount(); i++ ) {
    result = JoinStr( result, IncDirSwitch + m_CompilerDirectories[i], ' ' );
  }
  return result;
}

CString CBuildTarget::IncDirs( const CString& ProjectIncDirs, const CString& IncDirSwitch ) {
  return MakeOptions( m_IncludeDirectoriesRelation, ProjectIncDirs, IncDirs( IncDirSwitch ) );
}

CString CBuildTarget::ResDirs( const CString& IncDirSwitch ) {
  CString result;
  for( int i = 0; i < m_ResourceCompilerDirectories.GetCount(); i++ ) {
    result = JoinStr( result, IncDirSwitch + m_ResourceCompilerDirectories[i], ' ' );
  }
  return result;
}

CString CBuildTarget::ResDirs( const CString& ProjectResDirs, const CString& IncDirSwitch ) {
  return MakeOptions( m_ResourceIncludeDirectoriesRelation, ProjectResDirs, ResDirs( IncDirSwitch ) );
}

CString CBuildTarget::LibDirs( const CString& LibDirSwitch ) {
  CString result;
  for( int i = 0; i < m_LinkerDirectories.GetCount(); i++ ) {
    result = JoinStr( result, LibDirSwitch + m_LinkerDirectories[i], ' ' );
  }
  return result;
}

CString CBuildTarget::LibDirs( const CString& ProjectLibDirs, const CString& LibDirSwitch ) {
  return MakeOptions( m_LibraryDirectoriesRelation, ProjectLibDirs, LibDirs( LibDirSwitch ) );
}

CString CBuildTarget::Libs( const CPlatform& Platform, const CString& LinkLibSwitch ) {
  CString result;
  for( int i = 0; i < m_LinkerLibraries.GetCount(); i++ ) {
    CString lib_name = m_LinkerLibraries[i];
    CString lib_ext = ExtractFileExt( lib_name );
    if( Platform.IsStaticLibraryExtension( lib_ext ) ) {
      result = JoinStr( result, lib_name, ' ' );
    } else {
      result = JoinStr( result, LinkLibSwitch + lib_name, ' ' );
    }
  }
  return result;
}

CString CBuildTarget::ExtDeps( void ) {
  CString result;
  for( int i = 0; i < m_ExternalDependencies.GetCount(); i++ ) {
    result += " " + m_ExternalDependencies[i];
  }
  return result;
}

void CBuildTarget::Clear( void ) {
  m_Title = "default";
  m_Platforms.Clear();
  m_Output = "a.out";
  m_ObjectOutput.Clear();
  m_ExternalDependencies.Clear();
  m_Type = ttExecutable;
  m_AutoPrefix = false;
  m_AutoExtension = false;
  m_Compiler.Clear();
  m_CompilerOptions.Clear();
  m_CompilerDirectories.Clear();
  m_ResourceCompilerDirectories.Clear();
  m_LinkerOptions.Clear();
  m_LinkerLibraries.Clear();
  m_LinkerDirectories.Clear();
  m_BeforeBuildCommands.Clear();
  m_AfterBuildCommands.Clear();
  m_ForceBeforeBuildCommands = false;
  m_ForceAfterBuildCommands = false;
  m_CompilerOptionsRelation = orProjectTarget;
  m_LinkerOptionsRelation = orProjectTarget;
  m_IncludeDirectoriesRelation = orProjectTarget;
  m_ResourceIncludeDirectoriesRelation = orProjectTarget;
  m_LibraryDirectoriesRelation = orProjectTarget;
  m_UCName.Clear();
  m_LCName.Clear();
}

void CBuildTarget::Read( const TiXmlElement *TargetRoot ) {
  char *value = 0;
  if( ( value = ( char * )TargetRoot->Attribute( "title" ) ) ) {
    m_Title = value;
  }
  TiXmlNode *_option = ( TiXmlNode * )TargetRoot->FirstChild( "Option" );
  while( 0 != _option ) {
    TiXmlElement* option = _option->ToElement();
    if( 0 != option ) {
      char *value = 0;
      if( ( value = ( char * )option->Attribute( "platforms" ) ) ) {
        ParseStr( value, ';', m_Platforms );
        m_Platforms.RemoveEmpty();
        if( m_Platforms.FindString( "Windows" ) != INVALID_INDEX
            && m_Platforms.FindString( "MSys" ) == INVALID_INDEX ) {
          m_Platforms.Insert( "MSys" );
        }
      }
      if( ( value = ( char * )option->Attribute( "output" ) ) ) {
        m_Output = value;
      }
      if( ( value = ( char * )option->Attribute( "prefix_auto" ) ) ) {
        m_AutoPrefix = StringToBoolean( value );
      }
      if( ( value = ( char * )option->Attribute( "extension_auto" ) ) ) {
        m_AutoExtension = StringToBoolean( value );
      }
      if( ( value = ( char * )option->Attribute( "working_dir" ) ) ) {
        m_WorkingDirectory = value;
      }
      if( ( value = ( char * )option->Attribute( "object_output" ) ) ) {
        m_ObjectOutput = value;
      }
      if( ( value = ( char * )option->Attribute( "external_deps" ) ) ) {
        ParseStr( value, ';', m_ExternalDependencies );
        m_ExternalDependencies.RemoveEmpty();
      }
      if( ( value = ( char * )option->Attribute( "type" ) ) ) {
        m_Type = ( CBuildTarget::TargetType )StringToInteger( value );
      }
      if( ( value = ( char * )option->Attribute( "compiler" ) ) ) {
        m_Compiler = value;
      }
      if( ( value = ( char * )option->Attribute( "projectCompilerOptionsRelation" ) ) ) {
        m_CompilerOptionsRelation = ( CBuildTarget::OptionsRelation )StringToInteger( value );
      }
      if( ( value = ( char * )option->Attribute( "projectLinkerOptionsRelation" ) ) ) {
        m_LinkerOptionsRelation = ( CBuildTarget::OptionsRelation )StringToInteger( value );
      }
      if( ( value = ( char * )option->Attribute( "projectIncludeDirsRelation" ) ) ) {
        m_IncludeDirectoriesRelation = ( CBuildTarget::OptionsRelation )StringToInteger( value );
      }
      if( ( value = ( char * )option->Attribute( "projectResourceIncludeDirsRelation" ) ) ) {
        m_ResourceIncludeDirectoriesRelation = ( CBuildTarget::OptionsRelation )StringToInteger( value );
      }
      if( ( value = ( char * )option->Attribute( "projectLibDirsRelation" ) ) ) {
        m_LibraryDirectoriesRelation = ( CBuildTarget::OptionsRelation )StringToInteger( value );
      }
    }
    _option = ( TiXmlNode * )TargetRoot->IterateChildren( _option );
  }
  TiXmlNode *_compiler = ( TiXmlNode * )TargetRoot->FirstChild( "Compiler" );
  if( 0 != _compiler ) {
    TiXmlNode *_option = ( TiXmlNode * )_compiler->FirstChild( "Add" );
    while( 0 != _option ) {
      TiXmlElement* option = _option->ToElement();
      if( 0 != option ) {
        char *value = 0;
        if( ( value = ( char * )option->Attribute( "option" ) ) ) {
          m_CompilerOptions.Insert( value );
        }
        if( ( value = ( char * )option->Attribute( "directory" ) ) ) {
          m_CompilerDirectories.Insert( value );
        }
      }
      _option = ( TiXmlNode * )_compiler->IterateChildren( _option );
    }
  }
  TiXmlNode *_res_compiler = ( TiXmlNode * )TargetRoot->FirstChild( "ResourceCompiler" );
  if( 0 != _res_compiler ) {
    TiXmlNode *_option = ( TiXmlNode * )_res_compiler->FirstChild( "Add" );
    while( 0 != _option ) {
      TiXmlElement* option = _option->ToElement();
      if( 0 != option ) {
        char *value = 0;
        if( ( value = ( char * )option->Attribute( "directory" ) ) ) {
          m_ResourceCompilerDirectories.Insert( value );
        }
      }
      _option = ( TiXmlNode * )_res_compiler->IterateChildren( _option );
    }
  }
  TiXmlNode *_linker = ( TiXmlNode * )TargetRoot->FirstChild( "Linker" );
  if( 0 != _linker ) {
    TiXmlNode *_option = ( TiXmlNode * )_linker->FirstChild( "Add" );
    while( 0 != _option ) {
      TiXmlElement* option = _option->ToElement();
      if( 0 != option ) {
        if( strcmp( option->Value(), "Add" ) ) {
          break;
        }
        char *value = 0;
        if( ( value = ( char * )option->Attribute( "option" ) ) ) {
          m_LinkerOptions.Insert( value );
        }
        if( ( value = ( char * )option->Attribute( "directory" ) ) ) {
          m_LinkerDirectories.Insert( value );
        }
        if( ( value = ( char * )option->Attribute( "library" ) ) ) {
          m_LinkerLibraries.Insert( value );
        }
      }
      _option = ( TiXmlNode * )_linker->IterateChildren( _option );
    }
  }
  TiXmlNode *_extra_cmd = ( TiXmlNode * )TargetRoot->FirstChild( "ExtraCommands" );
  if( 0 != _extra_cmd ) {
    TiXmlNode *_option = ( TiXmlNode * )_extra_cmd->FirstChild( "Add" );
    while( 0 != _option ) {
      TiXmlElement* option = _option->ToElement();
      if( 0 != option ) {
        if( strcmp( option->Value(), "Add" ) ) {
          break;
        }
        char *value = 0;
        if( ( value = ( char * )option->Attribute( "before" ) ) ) {
          m_BeforeBuildCommands.Insert( value );
        }
        if( ( value = ( char * )option->Attribute( "after" ) ) ) {
          m_AfterBuildCommands.Insert( value );
        }
      }
      _option = ( TiXmlNode * )_extra_cmd->IterateChildren( _option );
    }
    _option = ( TiXmlNode * )_extra_cmd->FirstChild( "Mode" );
    while( 0 != _option ) {
      TiXmlElement* option = _option->ToElement();
      if( 0 != option ) {
        if( strcmp( option->Value(), "Mode" ) ) {
          break;
        }
        char *value = 0;
        if( ( value = ( char * )option->Attribute( "before" ) ) ) {
          m_ForceBeforeBuildCommands = ( strcmp( value, "always" ) == 0 );
        }
        if( ( value = ( char * )option->Attribute( "after" ) ) ) {
          m_ForceAfterBuildCommands = ( strcmp( value, "always" ) == 0 );
        }
      }
      _option = ( TiXmlNode * )_extra_cmd->IterateChildren( _option );
    }
  }
  // decorate target name
  {
    m_MFName = MakefileFriendly( m_Title );
    m_UCName = UpperCase( m_MFName );
    m_LCName = LowerCase( m_MFName );
    m_ObjectOutput = RemoveTrailingPathDelimiter( m_ObjectOutput );
  }
}

CString CBuildTarget::Name( const CString& Prefix, const int Case ) const {
  switch( Case ) {
    default:
    case 0:
      return Prefix + m_MFName;
    case 1:
      return LowerCase( Prefix ) + m_LCName;
    case 2:
      return UpperCase( Prefix ) + m_UCName;
  }
}

CString CBuildTarget::Name( const CString& Prefix ) const {
  switch( m_NameCase ) {
    default:
    case 0:
      return Prefix + m_MFName;
    case 1:
      return LowerCase( Prefix ) + m_LCName;
    case 2:
      return UpperCase( Prefix ) + m_UCName;
  }
}

void CBuildTarget::Show( void ) {
  std::cout << "Target title: " << m_Title.GetString() << std::endl;
  if( m_Platforms.GetCount() ) {
    ShowStringList( "Platforms", "Platform", m_Platforms );
  } else {
    std::cout << "Platforms: all" << std::endl;
  }
  std::cout << "Binary output: " << m_Output.GetCString() << std::endl;
  std::cout << "Automatic prefix: " << BooleanToYesNoString( m_AutoPrefix ).GetCString() << std::endl;
  std::cout << "Automatic extension: " << BooleanToYesNoString( m_AutoExtension ).GetCString() << std::endl;
  std::cout << "Working directory: " << m_WorkingDirectory.GetCString() << std::endl;
  std::cout << "Object output: " << m_ObjectOutput.GetCString() << std::endl;
  ShowStringList( "External dependencies", "Dependency", m_ExternalDependencies );
  std::cout << "Type: " << TargetTypeName().GetCString() << std::endl;
  std::cout << "Compiler: " << m_Compiler.GetCString() << std::endl;
  std::cout << "Compiler options relation: "
            << OptionsRelationName( m_CompilerOptionsRelation ).GetCString() << std::endl;
  std::cout << "Linker options relation: "
            << OptionsRelationName( m_LinkerOptionsRelation ).GetCString() << std::endl;
  std::cout << "Include directories relation: "
            << OptionsRelationName( m_IncludeDirectoriesRelation ).GetCString() << std::endl;
  std::cout << "Resource include directories relation: "
            << OptionsRelationName( m_ResourceIncludeDirectoriesRelation ).GetCString() << std::endl;
  std::cout << "Library directories relation: "
            << OptionsRelationName( m_LibraryDirectoriesRelation ).GetCString() << std::endl;
  ShowStringList( "Compiler options", "Option", m_CompilerOptions );
  ShowStringList( "Compiler directories", "Directory", m_CompilerDirectories );
  ShowStringList( "Resource compiler directories", "Directory", m_ResourceCompilerDirectories );
  ShowStringList( "Linker options", "Option", m_LinkerOptions );
  ShowStringList( "Linker directories", "Directory", m_LinkerDirectories );
  ShowStringList( "Linked libraries", "Library", m_LinkerLibraries );
  ShowStringList( "Before-build commands", "Command", m_BeforeBuildCommands );
  std::cout << "Force before-build commands: "
            << BooleanToYesNoString( m_ForceBeforeBuildCommands ).GetCString() << std::endl;
  ShowStringList( "After-build commands", "Command", m_AfterBuildCommands );
  std::cout << "Force after-build commands: "
            << BooleanToYesNoString( m_ForceAfterBuildCommands ).GetCString() << std::endl;
}


CVirtualTarget::CVirtualTarget( void ) {
  Clear();
}

CVirtualTarget::~CVirtualTarget( void ) {
  Clear();
}

void CVirtualTarget::Clear( void ) {
  m_Alias.Clear();
  m_Targets.Clear();
  m_UCName.Clear();
  m_LCName.Clear();
}

void CVirtualTarget::Read( const TiXmlElement *TargetRoot ) {
  char *value = 0;
  if( ( value = ( char * )TargetRoot->Attribute( "alias" ) ) ) {
    m_Alias = value;
  }
  if( ( value = ( char * )TargetRoot->Attribute( "targets" ) ) ) {
    ParseStr( value, ';', m_Targets );
    m_Targets.RemoveEmpty();
  }
  // decorate target name
  {
    m_MFName = MakefileFriendly( m_Alias );
    m_UCName = UpperCase( m_MFName );
    m_LCName = LowerCase( m_MFName );
  }
}

CString CVirtualTarget::Name( const CString& Prefix ) const {
  switch( m_NameCase ) {
    default:
    case 0:
      return Prefix + m_MFName;
    case 1:
      return LowerCase( Prefix ) + m_LCName;
    case 2:
      return UpperCase( Prefix ) + m_UCName;
  }
}

CString CVirtualTarget::Name( const CString& Prefix, const int TargetIndex ) const {
  ( void )Prefix;
  CString name = MakefileFriendly( m_Targets.GetString( TargetIndex ) );
  switch( m_NameCase ) {
    default:
    case 0:
      return name;
    case 1:
      return LowerCase( name );
    case 2:
      return UpperCase( name );
  }
}

void CVirtualTarget::Show( void ) {
  std::cout << "Target alias: " << m_Alias.GetString() << std::endl;
  if( m_Targets.GetCount() ) {
    ShowStringList( "Targets", "Target", m_Targets );
  }
}

//------------------------------------------------------------------------------
